/*-------------------------------------------------------------------------
 * CLR版本:     4.0.30319.42000
 * 创建人:      song
 * 创建时间：   2024/4/9 17:56:20
 * 功 能：     自动折叠汇总功能 没有在折叠功能的会隐藏
 *-------------------------------------------------------------------------*/

using UnityEngine;

namespace Function.SerializedPropertyAttribute
{
    public class FoldoutMustAttribute : PropertyAttribute
    {
        public readonly string _name;
        public readonly bool _foldEverything;
        public readonly bool needDrawNotSAttribute;

        public FoldoutMustAttribute(string name, bool foldEverything = false, bool needDrawNotSAttribute = true)
        {
            _foldEverything = foldEverything;
            _name = name;
        }
    }
}

#if UNITY_EDITOR
namespace Function.SerializedPropertyAttribute
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using UnityEditor;

    public class FoldoutMustAttributeHelper
    {
        private readonly Dictionary<string, MustCacheFoldProp> _cacheFoldouts =
            new Dictionary<string, MustCacheFoldProp>();

        private readonly List<SerializedProperty> _props = new List<SerializedProperty>();
        private bool _initialized;

        private readonly UnityEngine.Object _target;
        private readonly SerializedObject _serializedObject;

        public bool OverrideInspector => _cacheFoldouts.Count > 0;

        public FoldoutMustAttributeHelper(UnityEngine.Object target, SerializedObject serializedObject)
        {
            _target = target;
            _serializedObject = serializedObject;
        }

        public void OnDisable()
        {
            if (_target == null) return;

            foreach (var c in _cacheFoldouts)
            {
                EditorPrefs.SetBool(
                    string.Format($"{c.Value.Attribute._name}{c.Value.Properties[0].name}{_target.name}"),
                    c.Value.Expanded);
                c.Value.Dispose();
            }
        }

        public void Update()
        {
            _serializedObject.Update();
            Setup();
        }

        public void OnInspectorGUI()
        {
            Header();
            Body();

            _serializedObject.ApplyModifiedProperties();
        }

        private void Header()
        {
            using (new EditorGUI.DisabledScope("m_Script" == _props[0].propertyPath))
            {
                EditorGUILayout.Space();
                EditorGUILayout.PropertyField(_props[0], true);
                EditorGUILayout.Space();
            }
        }

        private void Body()
        {
            foreach (var pair in _cacheFoldouts)
            {
                EditorGUILayout.BeginVertical(StyleFramework.Box);
                Foldout(pair.Value);
                EditorGUILayout.EndVertical();

                EditorGUI.indentLevel = 0;
            }

            EditorGUILayout.Space();
        }

        private void Foldout(MustCacheFoldProp cache)
        {
            cache.Expanded =
                EditorGUILayout.Foldout(cache.Expanded, cache.Attribute._name, true, StyleFramework.FoldoutHeader);
            var rect = GUILayoutUtility.GetLastRect();
            rect.x -= 18;
            rect.y -= 4;
            rect.height += 8;
            rect.width += 22;
            EditorGUI.LabelField(rect, GUIContent.none, EditorStyles.helpBox);

            if (cache.Expanded)
            {
                EditorGUILayout.Space(2);

                foreach (var property in cache.Properties)
                {
                    EditorGUILayout.BeginVertical(StyleFramework.BoxChild);
                    EditorGUILayout.PropertyField(property,
                        new GUIContent(ObjectNames.NicifyVariableName(property.name)), true);
                    EditorGUILayout.EndVertical();
                }
            }
        }

        private void Setup()
        {
            if (_initialized) return;

            FoldoutMustAttribute prevFold = default;

            var length = EditorTypes.Get(_target, out var objectFields);

            for (var i = 0; i < length; i++)
            {
                #region FOLDERS

                var fold = Attribute.GetCustomAttribute(objectFields[i],
                    typeof(FoldoutMustAttribute)) as FoldoutMustAttribute;
                MustCacheFoldProp c;
                if (fold == null)
                {
                    if (prevFold != null && prevFold._foldEverything)
                    {
                        if (!_cacheFoldouts.TryGetValue(prevFold._name, out c))
                        {
                            _cacheFoldouts.Add(prevFold._name,
                                new MustCacheFoldProp
                                    { Attribute = prevFold, Types = new HashSet<string> { objectFields[i].Name } });
                        }
                        else
                        {
                            c.Types.Add(objectFields[i].Name);
                        }
                    }

                    continue;
                }

                prevFold = fold;

                if (!_cacheFoldouts.TryGetValue(fold._name, out c))
                {
                    var expanded =
                        EditorPrefs.GetBool(string.Format($"{fold._name}{objectFields[i].Name}{_target.name}"), false);
                    _cacheFoldouts.Add(fold._name,
                        new MustCacheFoldProp
                        {
                            Attribute = fold, Types = new HashSet<string> { objectFields[i].Name }, Expanded = expanded
                        });
                }
                else c.Types.Add(objectFields[i].Name);

                #endregion
            }

            var property = _serializedObject.GetIterator();
            var next = property.NextVisible(true);
            if (next)
            {
                do
                {
                    HandleFoldProp(property);
                } while (property.NextVisible(false));
            }

            _initialized = true;
        }

        private void HandleFoldProp(SerializedProperty prop)
        {
            bool shouldBeFolded = false;

            foreach (var pair in _cacheFoldouts)
            {
                if (pair.Value.Types.Contains(prop.name))
                {
                    var pr = prop.Copy();
                    shouldBeFolded = true;
                    pair.Value.Properties.Add(pr);

                    break;
                }
            }

            if (shouldBeFolded == false)
            {
                var pr = prop.Copy();
                _props.Add(pr);
            }
        }

        private class MustCacheFoldProp
        {
            public HashSet<string> Types = new HashSet<string>();
            public readonly List<SerializedProperty> Properties = new List<SerializedProperty>();
            public FoldoutMustAttribute Attribute;
            public bool Expanded;

            public void Dispose()
            {
                Properties.Clear();
                Types.Clear();
                Attribute = null;
            }
        }
    }
}
#endif